#ifndef __FILE_HASH__
#define __FILE_HASH__

#include "md5.hxx"
#include "../petrk/wpefio.h"

namespace mm
{
	template < size_t FRAME_SIZE, class Hasher >
	long travel_file_data( wpe::FileObject * kfs, Hasher& hasher )
	{
		uint64_t fs64 = kfs->get_length();
		if( fs64 == wpe::FileObject::InvalidLength ) 
			return E_FAIL;

		std::vector<uint8_t> buffer(FRAME_SIZE);
		while( fs64 )
		{
			size_t to_read = (size_t)min( fs64, (uint64_t)FRAME_SIZE );
			size_t eff = 0;
			RFAILED( kfs->read( buffer.data(), to_read, &eff ) );
			if( !hasher.update( buffer.data(), eff ) ) return S_FALSE;
			fs64 -= eff;
		}
		return S_OK;
	}

	struct hashes
	{
		uint8_t			md5_[16];
		md5::MD5_CTX	md5_ctx;

		hashes()
		{
			init();
		}

		uint8_t * get_md5( char * md5_str = 0 )
		{
			if( md5_str )
			{
				for( size_t i = 0; i < sizeof(md5_); ++ i )
					sprintf( md5_str + (i<<1), "%02X", md5_[i] );
			}
			return md5_;
		}

	public:

		bool update( const void * buf, size_t bytes )
		{
			md5::MD5Update( &md5_ctx, (const uint8_t*)buf, bytes );
			return true;
		}
		long calc( wpe::FileObject * pfo )
		{
			RFAILED( (travel_file_data<0x10000,hashes&>( pfo, *this )) );
			return checkout();
		}
	protected:

		long init()
		{
			md5::MD5Init(&md5_ctx);
			return S_OK;
		}
		long checkout()
		{
			md5::MD5Final( md5_, &md5_ctx );

			return S_OK;
		}
	};

	long get_file_md5( wpe::FileObject * file, std::string & str_md5 )
	{
		hashes h;
		RFAILED( h.calc( file ) );
		char md5_str[33] = {};
		h.get_md5( md5_str );
		str_md5 = md5_str;
		return S_OK;
	}
};

#endif